package ua.edu.sumdu.j2se.roman.lab1.domain;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
 * Author: Radko Roman.
 */

/**
 * This class describes task.
 *
 */
public class Task implements Cloneable, Serializable {

    /**
     * Creates one time task.
     *
     * @param title task title
     * @param time  task start time
     * @throws IllegalArgumentException Title can not be null and time can not be less then 0.
     */
    public Task(String title, Date time) {
        if (time == null) {
            throw new IllegalArgumentException("Time parameter shouldn't be null");
        }
        // in case if title is null or time parameter < 0
        if (title == null) {
            throw new IllegalArgumentException("Title can not be null and time can not be less then 0");
        }
        this.title = title;
        this.startTime = time;
        this.endTime = time;
        this.repeat = 0;
    }

    /**
     * Creates repeatable task.
     *
     * @param title     task title, not null
     * @param startTime task start time
     * @param endTime   task end time
     * @param repeat    repeat interval,  >= 0
     * @throws IllegalArgumentException Task parameter should be real.
     */
    public Task(String title, Date startTime, Date endTime, long repeat) {
        // in case if startTime > endTime or startTime < 0 or title is null or repeat < 0
        if (startTime.after(endTime) || title == null || repeat < 0) {
            throw new IllegalArgumentException("Task parameter should be real");
        }
        this.title = title;
        this.startTime = startTime;
        this.endTime = endTime;
        this.repeat = repeat;
    }


    /**
     * This method gets title of the task.
     *
     * @return title of the task
     */
    public String getTitle() {
        return title;
    }

    /**
     * This method sets title of the task.
     *
     * @param taskTitle task title, not null
     * @throws IllegalArgumentException Title shouldn't be null.
     */
    public void setTitle(String taskTitle) {
        // in case if setting title is null
        if (taskTitle == null) {
            throw new IllegalArgumentException("Title shouldn't be null");
        }
        this.title = taskTitle;
    }

    /**
     * This method checks task active or not.
     *
     * @return true, if task is active
     */
    public boolean isActive() {
        return active;
    }

    /**
     * This method set status of the task.
     *
     * @param active true, if task is active
     */
    public void setActive(boolean active) {
        this.active = active;
    }

    /**
     * Set time for one time task.
     *
     * @param time task start time
     * @throws IllegalArgumentException Time shouldn't be less then zero.
     */
    public void setTime(Date time) {
        if (time == null) {
            throw new IllegalArgumentException("Time parameter shouldn't be null");
        }
        this.startTime = time;
        this.endTime = time;
        repeat = 0;
    }

    /**
     * Set start time, end time, repeat interval of task.
     *
     *
     * @param taskStart task start time
     * @param endTime   task end time
     * @param repeat    time interval
     * @throws IllegalArgumentException Task parameter should be real.
     */
    public void setTime(Date taskStart, Date endTime, Long repeat) {
        // in case if startTime parameter > endTime parameter or repeat < 0 or startTime < 0
        if (taskStart.after(endTime) || repeat < 0 /*taskStart < 0*/) {
            throw new IllegalArgumentException("Task parameter should be real");
        }
        this.startTime = taskStart;
        this.endTime = endTime;
        this.repeat = repeat;
    }

    /**
     * Return task start time.
     *
     * @return start time
     */
    public Date getTime() {
        return startTime;
    }

    /**
     * Return start time of the task.
     *
     * @return start time task
     */
    public Date getStartTime() {
        return startTime;
    }

    /**
     * Return end time of the task.
     *
     * @return end time task
     */
    public Date getEndTime() {
        return endTime;
    }

    /**
     * Check if task is repeatable.
     */
    public boolean isRepeated() {
        return repeat != 0;
    }

    /**
     * Get repeat interval of the task.
     *
     * @return repeat interval
     */
    public long getRepeatInterval() {
        return repeat;
    }

    /**
     * Next event time after specified time point.
     *
     *
     * @param time specified time point
     * @return returns next event time if it exists, if not then it returns -1
     * @throws IllegalArgumentException Task parameter should be real.
     */
    public Date nextTimeAfter(Date time) {
        //in case if time parameter = null
        if (time == null) {
            throw new IllegalArgumentException("Time parameter shouldn't be null");
        }
        if (!active) {
            return null;
        }   
        long r = repeat * 1000; 
        if (startTime.equals(endTime)) {
            if (time.before(startTime)) {
                return startTime;
            }
            return null;
        }
        for (long t = startTime.getTime(); t + r <= endTime.getTime(); t += r) {
            if (time.getTime() >= t && (time.getTime() < t + r)) {
                return new Date(t + r);
            }
        }
        if (time.before(startTime)) {
            return startTime;
        }
        return null;
    }

    /**
     * Makes description of the task.
     *
     * @return text string
     */
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("Task ")
                .append("\"")
                .append(this.title)
                .append("\" ");
        if (!active) {
            sb.append("(inactive) at ")
                    .append(startTime);
        } else {
            sb.append("from ")
                    .append(startTime)
                    .append(" to ")
                    .append(endTime)
                    .append(" every ")
                    .append(repeat)
                    .append(" s");
        }
        return sb.toString();
    }

    /**
     * This method determines whether the startTime or startTime + repeat in interval between parameters from and to.
     *
     * @param from initial time point
     * @param to   final time point
     * @return is in interval or not
     */
    public boolean isInInterval(Date from, Date to) {
        if (!active)
            return false;        
        if (!isRepeated()) {
            return from.before(startTime) && ((startTime.before(to)) || startTime.equals(to));
        }
        for (long t = startTime.getTime(); t <= endTime.getTime(); t += repeat*1000) {
            if (from.getTime() < t && (t <= to.getTime())) {
                return true;
            }
        }
        return false;
    }

    /**
     * This method cloned task.
     *
     * @return clone of task parameter
     * @throws CloneNotSupportedException Thrown to indicate that the clone method in class Object has been called to clone an
     *                                    object, but that the object's class does not implement the Cloneable interface.
     */
    public Object clone() throws CloneNotSupportedException {
        // Initializing clone of task
        Task clonedTask = (Task) super.clone();

        return clonedTask;
    }

    /**
     * This method overriding standard equals method which specified for Task object.
     *
     * @param other some object
     * @return      boolean
     */
    @Override
    public boolean equals(Object other) {
        if (this == other) return true;
        if (other == null || getClass() != other.getClass()) return false;

        Task task = (Task) other;

        if (active != task.active) return false;
        if (!(endTime.equals(task.endTime))) return false;
        if (repeat != task.repeat) return false;
        if (!(startTime.equals(task.startTime))) return false;
        if (title != null ? !title.equals(task.title) : task.title != null) return false;

        return true;
    }

    /**
     * This method overriding standard hashCode method which specified for Task object.
     *
     * @return int hashCode of the Task object
     */
    @Override
    public int hashCode() {
        long result = repeat;
        result = 31 * result + (active ? 1 : 0);
        result = 31 * result + (title != null ? title.hashCode() : 0);
        result = 31 * result + startTime.getTime();
        result = 31 * result + endTime.getTime();
        return (int) result;
    }

    /**
     * This method add to the ArrayList all dates which contains in borders: start < date <= end
     *
     * @param start Date
     * @param end   Date
     * @return      ArrayList<Date>
     */
    public List<Date> getDatesInTask (Date start, Date end) {
        List<Date> dateKeys = new ArrayList<Date>();

        for (long point = startTime.getTime(); point <= end.getTime(); point += repeat*1000) {
            if (start.getTime() < point) {
                dateKeys.add(new Date(point));
            }
        }
        return dateKeys;
    }

    /**
     * Repeat time for the task.
     */
    long repeat;

    /**
     * Flag of task activity.
     */
    private boolean active;

    /**
     * Task title.
     */
    private String title;

    /**
     * Start time of the task.
     */
    private Date startTime;

    /**
     * End time of the task.
     */
    private Date endTime;

}

